译者注:已经很少用React写东西了,但遇到好的文章还是想推荐出来...
文章译自 Eyk Rehbein 所写的Fetch images with a React hook[1]。
我们都知道获取和显示图片的默认方式:
<img src="/image.jpg" />
整个行业的框架和库都在使用这种方式。但是,如果你想改善用户体验,在图片还在加载的时候显示loading效果;或者在加载出错时给出提示信息,怎么办?不幸的是,在默认的图片加载方式下,这并不容易实现。
不过不用担心,解决方法是有的。我们可以直接用 React Hook 并行加载图片,它还能告诉你图片何时加载完以及是否出现错误。
首先,我想介绍一下该 Hook:
import { useState, useEffect } from "react";
export const useImage = (src: string) => {
const [hasLoaded, setHasLoaded] = useState(false);
const [hasError, setHasError] = useState(false);
const [hasStartedInitialFetch, setHasStartedInitialFetch] = useState(false);
useEffect(() => {
setHasStartedInitialFetch(true);
setHasLoaded(false);
setHasError(false);
// Here's where the magic happens.
// 奇迹在这里
const image = new Image();
image.src = src;
const handleError = () => {
setHasError(true);
};
const handleLoad = () => {
setHasLoaded(true);
setHasError(false);
};
image.onerror = handleError;
image.onload = handleLoad;
return () => {
image.removeEventListener("error", handleError);
image.removeEventListener("load", handleLoad);
};
}, [src]);
return { hasLoaded, hasError, hasStartedInitialFetch };
};
这个 Hook 做了什么呢?好吧,它基本上是把图片加载到缓存中,或者从缓存中取出图片(如果已经加载过)。
同时,它还提供了一些有用的信息,比如图像是否已经加载完成、加载过程中是否发生了错误、或者初次加载是否开始。然后,只要图像还没有加载完成,你就可以利用这些信息在图像的位置上放置一个加载指示。一个使用了该Hook的例子可以像下面这样:
const Demo = () => {
const imageUrl = "/image.png";
const { hasLoaded, hasError } = useImage(imageUrl);
if (hasError) {
return null;
}
return (
<>
...
{!hasLoaded && <LoadingIndicator />}
{hasLoaded && <img src={imageUrl} />}
</>
);
};
并非如此,尽管人们预期会这样,毕竟上例的 Hook 和 img 都引用了该图片源(imageUrl)。然而,无论图片源的引用频率有多高,都只会发送一个请求。这带来的好处是,该图片源无论在页面的哪里引用,hasLoaded 总是会变成 true(假设加载是成功的)。
如果你想以一种简单的方式加载图像,同时又能知道确切的加载状态,我强烈推荐该 Hook。事实上,typeafe.blog上的所有文章缩略图都是用这个Hook加载的。如果你网络不好,你可以看到一个加载提示(loading indicator),直到图像加载完成。
Fetch images with a React hook: https://typesafe.blog/article/fetch-images-with-a-react-hook